home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / g__~1 / gplibs20.zoo / filebuf.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-13  |  16.3 KB  |  632 lines

  1. //    This is part of the iostream library, providing input/output for C++.
  2. //    Copyright (C) 1991, 1992 Per Bothner.
  3. //
  4. //    This library is free software; you can redistribute it and/or
  5. //    modify it under the terms of the GNU Library General Public
  6. //    License as published by the Free Software Foundation; either
  7. //    version 2 of the License, or (at your option) any later version.
  8. //
  9. //    This library is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12. //    Library General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU Library General Public
  15. //    License along with this library; if not, write to the Free
  16. //    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include <ioprivat.h>
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <fcntl.h>
  22. #include <errno.h>
  23.  
  24. // An fstream can be in at most one of put mode, get mode, or putback mode.
  25. // Putback mode is a variant of get mode.
  26.  
  27. // In a filebuf, there is only one current position, instead of two
  28. // separate get and put pointers.  In get mode, the current posistion
  29. // is that of gptr(); in put mode that of pptr().
  30.  
  31. // The position in the buffer that corresponds to the position
  32. // in external file system is file_ptr().
  33. // This is normally egptr(), except in putback mode, when it is _save_egptr.
  34. // If the field _fb._offset is >= 0, it gives the offset in
  35. // the file as a whole corresponding to eGptr(). (???)
  36.  
  37. // PUT MODE:
  38. // If a filebuf is in put mode, pbase() is non-NULL and equal to base().
  39. // Also, epptr() == ebuf().
  40. // Also, eback() == gptr() && gptr() == egptr().
  41. // The un-flushed character are those between pbase() and pptr().
  42. // GET MODE:
  43. // If a filebuf is in get or putback mode, eback() != egptr().
  44. // In get mode, the unread characters are between gptr() and egptr().
  45. // The OS file position corresponds to that of egptr().
  46. // PUTBACK MODE:
  47. // Putback mode is used to remember "excess" characters that have
  48. // been sputbackc'd in a separate putback buffer.
  49. // In putback mode, the get buffer points to the special putback buffer.
  50. // The unread characters are the characters between gptr() and egptr()
  51. // in the putback buffer, as well as the area between save_gptr()
  52. // and save_egptr(), which point into the original reserve buffer.
  53. // (The pointers save_gptr() and save_egptr() are the values
  54. // of gptr() and egptr() at the time putback mode was entered.)
  55. // The OS position corresponds to that of save_egptr().
  56. //
  57. // LINE BUFFERED OUTPUT:
  58. // During line buffered output, pbase()==base() && epptr()==base().
  59. // However, ptr() may be anywhere between base() and ebuf().
  60. // This forces a call to filebuf::overflow(int C) on every put.
  61. // If there is more space in the buffer, and C is not a '\n',
  62. // then C is inserted, and pptr() incremented.
  63. //
  64. // UNBUFFERED STREAMS:
  65. // If a filebuf is unbuffered(), the _shortbuf[1] is used as the buffer.
  66.  
  67. #define CLOSED_FILEBUF_FLAGS \
  68.   (_S_IS_FILEBUF+_S_NO_READS+_S_NO_WRITES+_S_TIED_PUT_GET)
  69.  
  70. void filebuf::init()
  71. {
  72.     _fb._offset = 0;
  73.  
  74.     _link_in();
  75.     _fb._fileno = -1;
  76. }
  77.  
  78. filebuf::filebuf() : backupbuf(CLOSED_FILEBUF_FLAGS)
  79. {
  80.     init();
  81. }
  82.  
  83. filebuf::filebuf(int fd) : backupbuf(CLOSED_FILEBUF_FLAGS)
  84. {
  85.     init();
  86.     attach(fd);
  87. }
  88.  
  89. filebuf::filebuf(int fd, char* p, _G_size_t len) : backupbuf(CLOSED_FILEBUF_FLAGS)
  90. {
  91.     init();
  92.     attach(fd);
  93.     setbuf(p, len);
  94. }
  95.  
  96. filebuf::~filebuf()
  97. {
  98.     if (!(xflags() & _S_DELETE_DONT_CLOSE))
  99.     close();
  100.  
  101.     _un_link();
  102. }
  103.  
  104. #ifdef atarist
  105. extern "C" int __default_mode__;
  106. #endif
  107.  
  108. filebuf* filebuf::open(const char *filename, ios::openmode mode, int prot)
  109. {
  110.     if (is_open())
  111.     return NULL;
  112.     int posix_mode;
  113.     int read_write;
  114. #ifdef atarist
  115.     int rw_mode = (__default_mode__) ? _S_IS_BINARY : 0;
  116. #endif
  117.     if (mode & ios::app)
  118.     mode |= ios::out;
  119.     if ((mode & (ios::in|ios::out)) == (ios::in|ios::out)) {
  120.     posix_mode = O_RDWR;
  121.     read_write = 0;
  122.     }
  123.     else if (mode & ios::out)
  124.     posix_mode = O_WRONLY, read_write = _S_NO_READS;
  125.     else if (mode & (int)ios::in)
  126.     posix_mode = O_RDONLY, read_write = _S_NO_WRITES;
  127.     else
  128.     posix_mode = 0, read_write = _S_NO_READS+_S_NO_WRITES;
  129.     if ((mode & (int)ios::trunc) || mode == (int)ios::out)
  130.     posix_mode |= O_TRUNC;
  131.     if (mode & ios::app)
  132.     posix_mode |= O_APPEND, read_write |= _S_IS_APPENDING;
  133.     if (!(mode & (int)ios::nocreate) && mode != ios::in)
  134.     posix_mode |= O_CREAT;
  135.     if (mode & (int)ios::noreplace)
  136.     posix_mode |= O_EXCL;
  137.     int fd = ::open(filename, posix_mode, prot);
  138.     if (fd < 0)
  139.     return NULL;
  140.     _fb._fileno = fd;
  141. #ifdef atarist
  142.     if(mode & (int)ios::binary)
  143.     rw_mode |= _S_IS_BINARY;
  144.     else if(mode & (int)ios::text)
  145.     rw_mode &= ~_S_IS_BINARY;
  146.     read_write |= rw_mode;
  147.     xsetflags(read_write, (_S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING)|rw_mode);
  148. #else
  149.     xsetflags(read_write, _S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING);
  150. #endif
  151.     if (mode & (ios::ate|ios::app)) {
  152.     if (seekoff(0, ios::end) == EOF)
  153.         return NULL;
  154.     }
  155.     _link_in();
  156.     return this;
  157. }
  158.  
  159. filebuf* filebuf::open(const char *filename, const char *mode)
  160. {
  161.     if (is_open())
  162.     return NULL;
  163.     int oflags = 0, omode;
  164.     int read_write;
  165.     int oprot = 0666;
  166. #ifdef atarist
  167.     int rw_mode = (__default_mode__)? _S_IS_BINARY : 0;
  168. #endif
  169.     switch (*mode++) {
  170.       case 'r':
  171.     omode = O_RDONLY;
  172.     read_write = _S_NO_WRITES;
  173.     break;
  174.       case 'w':
  175.     omode = O_WRONLY;
  176.     oflags = O_CREAT|O_TRUNC;
  177.     read_write = _S_NO_READS;
  178.     break;
  179.       case 'a':
  180.     omode = O_WRONLY;
  181.     oflags = O_CREAT|O_APPEND;
  182.     read_write = _S_NO_READS|_S_IS_APPENDING;
  183.     break;
  184. #ifdef atarist
  185.       case 'b':
  186.     rw_mode |= _S_IS_BINARY;
  187.         break;
  188.       case 't':
  189.     rw_mode &= ~_S_IS_BINARY;
  190.     break;
  191. #endif
  192.       default:
  193.     errno = EINVAL;
  194.     return NULL;
  195.     }
  196.     if (mode[0] == '+' || (mode[0] == 'b' && mode[1] == '+')) {
  197.     omode = O_RDWR;
  198.     read_write &= _S_IS_APPENDING;
  199.     }
  200.     int fdesc = ::open(filename, omode|oflags, oprot);
  201.     if (fdesc < 0)
  202.     return NULL;
  203.     _fb._fileno = fdesc;
  204. #ifdef atarist
  205.     read_write |= rw_mode;
  206.     xsetflags(read_write, (_S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING)|rw_mode);
  207. #else
  208.     xsetflags(read_write, _S_NO_READS+_S_NO_WRITES+_S_IS_APPENDING);
  209. #endif
  210.     if (read_write & _S_IS_APPENDING)
  211.     if (seekoff(0, ios::end) == EOF)
  212.         return NULL;
  213.     _link_in();
  214.     return this;
  215. }
  216.  
  217. filebuf* filebuf::attach(int fd)
  218. {
  219.     if (is_open())
  220.     return NULL;
  221.     _fb._fileno = fd;
  222. #ifdef atarist
  223.     xsetflags((__default_mode__)? _S_IS_BINARY : 0,
  224.       (_S_NO_READS+_S_NO_WRITES) | ((__default_mode__) ? _S_IS_BINARY : 0));
  225. #else
  226.     xsetflags(0, _S_NO_READS+_S_NO_WRITES);
  227. #endif
  228.     return this;
  229. }
  230.  
  231. streambuf* filebuf::setbuf(char* p, _G_size_t len)
  232. {
  233.     if (streambuf::setbuf(p, len) == NULL)
  234.     return NULL;
  235.     setp(_base, _base);
  236.     setg(_base, _base, _base);
  237.     return this;
  238. }
  239.  
  240. int filebuf::overflow(int c)
  241. {
  242.     if (xflags() & _S_NO_WRITES) // SET ERROR
  243.     return EOF;
  244.     // Allocate a buffer if needed.
  245.     if (base() == NULL) {
  246.     doallocbuf();
  247.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(_base, _base);
  248.     else setp(_base, _ebuf);
  249.     setg(_base, _base, _base);
  250.     _flags |= _S_CURRENTLY_PUTTING;
  251.     }
  252.     // If currently reading, switch to writing.
  253.     else if ((_flags & _S_CURRENTLY_PUTTING) == 0) {
  254.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(gptr(), gptr());
  255.     else setp(gptr(), ebuf());
  256.     setg(egptr(), egptr(), egptr());
  257.     _flags |= _S_CURRENTLY_PUTTING;
  258.     }
  259.     if (c == EOF)
  260.     return do_flush();
  261.     if (pptr() == ebuf() ) // Buffer is really full
  262.     if (do_flush() == EOF)
  263.         return EOF;
  264.     xput_char(c);
  265.     if (unbuffered() || (linebuffered() && c == '\n'))
  266.     if (do_flush() == EOF)
  267.         return EOF;
  268.     return (unsigned char)c;
  269. }
  270.  
  271. int filebuf::underflow()
  272. {
  273. #if 0
  274.     /* SysV does not make this test; take it out for compatibility */
  275.     if (fp->_flags & __SEOF)
  276.     return (EOF);
  277. #endif
  278.  
  279.     if (xflags() & _S_NO_READS)
  280.     return EOF;
  281.     if (gptr() < egptr())
  282.     return *(unsigned char*)gptr();
  283.     allocbuf();
  284.  
  285.     // FIXME This can/should be moved to __streambuf ??
  286.     if ((xflags() & _S_LINE_BUF) || unbuffered()) {
  287.     // Flush all line buffered files before reading.
  288.     streambuf::flush_all_linebuffered();
  289.     }
  290.  
  291.     switch_to_get_mode();
  292.  
  293.     _G_ssize_t count = sys_read(base(), ebuf() - base());
  294.     if (count <= 0) {
  295.     if (count == 0)
  296.         xsetflags(_S_EOF_SEEN);
  297.     else
  298.         xsetflags(_S_ERR_SEEN), count = 0;
  299.     }
  300.     setg(base(), base(), base() + count);
  301.     setp(base(), base());
  302.     if (count == 0)
  303.     return EOF;
  304.     if (_fb._offset >= 0)
  305.     _fb._offset += count;
  306.     return *(unsigned char*)gptr();
  307. }
  308.  
  309. int filebuf::do_write(const char *data, _G_size_t to_do)
  310. {
  311.     if (to_do == 0)
  312.     return 0;
  313.     if (xflags() & _S_IS_APPENDING) {
  314.     // On a system without a proper O_APPEND implementation,
  315.     // you would need to sys_seek(0, ios::end) here, but is
  316.     // is not needed nor desirable for Unix- or Posix-like systems.
  317.     // Instead, just indicate that offset (before and after) is
  318.     // unpredictable.
  319.     _fb._offset = -1;
  320.     }
  321.     else if (egptr() != pbase()) {
  322.     long new_pos = sys_seek(pbase()-egptr(), ios::cur);
  323.     if (new_pos == -1)
  324.         return EOF;
  325.     _fb._offset = new_pos;
  326.     }
  327.     _G_ssize_t count = sys_write(data, to_do);
  328.     if (_cur_column)
  329.     _cur_column = __adjust_column(_cur_column - 1, data, to_do) + 1;
  330.     setg(base(), base(), base());
  331.     if (xflags() & _S_LINE_BUF+_S_UNBUFFERED) setp(base(), base());
  332.     else setp(base(), ebuf());
  333.     return count != to_do ? EOF : 0;
  334. }
  335.  
  336. int filebuf::sync()
  337. {
  338. //    char* ptr = cur_ptr();
  339.     if (pptr() > pbase())
  340.     if (do_flush()) return EOF;
  341.     if (gptr() != egptr()) {
  342.     streampos delta = gptr() - egptr();
  343.     if (in_backup())
  344.         delta -= eGptr() - Gbase();
  345.     _G_fpos_t new_pos = sys_seek(delta, ios::cur);
  346.     if (new_pos == EOF)
  347.       return EOF;
  348.     _fb._offset = new_pos;
  349.     setg(eback(), gptr(), gptr());
  350.     }
  351.     // FIXME: Cleanup - can this be shared?
  352. //    setg(base(), ptr, ptr);
  353.     return 0;
  354. }
  355.  
  356. #ifdef atarist
  357. # ifndef S_ISREG
  358. #  define S_ISREG(X) (((X) & S_IFMT) == S_IFREG)
  359. # endif
  360. #endif
  361.  
  362. streampos filebuf::seekoff(streamoff offset, _seek_dir dir, int mode)
  363. {
  364.     streampos result, new_offset, delta;
  365.     _G_ssize_t count;
  366.  
  367.     if (mode == 0) // Don't move any pointers.
  368.     dir = ios::cur, offset = 0;
  369.  
  370.     // Flush unwritten characters.
  371.     // (This may do an unneeded write if we seek within the buffer.
  372.     // But to be able to switch to reading, we would need to set
  373.     // egptr to ptr.  That can't be done in the current design,
  374.     // which assumes file_ptr() is eGptr.  Anyway, since we probably
  375.     // end up flushing when we close(), it doesn't make much difference.)
  376.     if (pptr() > pbase() || put_mode())
  377.     if (switch_to_get_mode()) return EOF;
  378.  
  379.     if (base() == NULL) {
  380.     doallocbuf();
  381.     setp(base(), base());
  382.     setg(base(), base(), base());
  383.     }
  384.     switch (dir) {
  385.       case ios::cur:
  386.     if (_fb._offset < 0) {
  387.         _fb._offset = sys_seek(0, ios::cur);
  388.         if (_fb._offset < 0)
  389.         return EOF;
  390.     }
  391.     // Make offset absolute, assuming current pointer is file_ptr().
  392.     offset += _fb._offset;
  393.  
  394.     offset -= _egptr - _gptr;
  395.     if (in_backup())
  396.         offset -= _other_egptr - _other_gbase;
  397.     dir = ios::beg;
  398.     break;
  399.       case ios::beg:
  400.     break;
  401.       case ios::end:
  402.     struct stat st;
  403.     if (sys_stat(&st) == 0 && S_ISREG(st.st_mode)) {
  404.         offset += st.st_size;
  405.         dir = ios::beg;
  406.     }
  407.     else
  408.         goto dumb;
  409.     }
  410.     // At this point, dir==ios::beg.
  411.  
  412.     // If destination is within current buffer, optimize:
  413.     if (_fb._offset >= 0 && _eback != NULL) {
  414.     // Offset relative to start of main get area.
  415.     _G_fpos_t rel_offset = offset - _fb._offset
  416.         + (eGptr()-Gbase());
  417.     if (rel_offset >= 0) {
  418.         if (in_backup())
  419.         switch_to_main_get_area();
  420.         if (rel_offset <= _egptr - _eback) {
  421.         setg(base(), base() + rel_offset, egptr());
  422.         setp(base(), base());
  423.         return offset;
  424.         }
  425.         // If we have streammarkers, seek forward by reading ahead.
  426.         if (have_markers()) {
  427.         long to_skip = rel_offset - (_gptr - _eback);
  428.         if (ignore(to_skip) != to_skip)
  429.             goto dumb;
  430.         return offset;
  431.         }
  432.     }
  433.     if (rel_offset < 0 && rel_offset >= Bbase() - Bptr()) {
  434.         if (!in_backup())
  435.         switch_to_backup_area();
  436.         gbump(_egptr + rel_offset - gptr());
  437.         return offset;
  438.     }
  439.     }
  440.  
  441.     unsave_markers();
  442.  
  443.     // Try to seek to a block boundary, to improve kernel page management.
  444.     new_offset = offset & ~(ebuf() - base() - 1);
  445.     delta = offset - new_offset;
  446.     if (delta > ebuf() - base()) {
  447.     new_offset = offset;
  448.     delta = 0;
  449.     }
  450.     result = sys_seek(new_offset, ios::beg);
  451.     if (result < 0)
  452.     return EOF;
  453.     if (delta == 0)
  454.     count = 0;
  455.     else {
  456.     count = sys_read(base(), ebuf()-base());
  457.     if (count < delta) {
  458.         // We weren't allowed to read, but try to seek the remainder.
  459.         offset = count == EOF ? delta : delta-count;
  460.         dir = ios::cur;
  461.         goto dumb;
  462.     }
  463.     }
  464.     setg(base(), base()+delta, base()+count);
  465.     setp(base(), base());
  466.     _fb._offset = result + count;
  467.     xflags(xflags() & ~ _S_EOF_SEEN);
  468.     return offset;
  469.   dumb:
  470.     unsave_markers();
  471.     result = sys_seek(offset, dir);
  472.     if (result != EOF) {
  473.     xflags(xflags() & ~_S_EOF_SEEN);
  474.     }
  475.     _fb._offset = result;
  476.     setg(base(), base(), base());
  477.     setp(base(), base());
  478.     return result;
  479. }
  480.  
  481. filebuf* filebuf::close()
  482. {
  483.     if (!is_open())
  484.     return NULL;
  485.  
  486.     // This flushes as well as switching mode.
  487.     if (pptr() > pbase() || put_mode())
  488.     if (switch_to_get_mode()) return NULL;
  489.  
  490.     unsave_markers();
  491.  
  492.     int status = sys_close();
  493.  
  494.     // Free buffer.
  495.     setb(NULL, NULL, 0);
  496.     setg(NULL, NULL, NULL);
  497.     setp(NULL, NULL);
  498.  
  499.     _un_link();
  500.     _flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
  501.     _fb._fileno = EOF;
  502.     _fb._offset = 0;
  503.  
  504.     return status < 0 ? NULL : this;
  505. }
  506.  
  507. _G_ssize_t filebuf::sys_read(char* buf, size_t size)
  508. {
  509.     for (;;) {
  510. #ifdef atarist
  511.     _G_ssize_t count = ::_read(_fb._fileno, buf, size);
  512. #else
  513.     _G_ssize_t count = ::read(_fb._fileno, buf, size);
  514. #endif
  515.     if (count != -1 || errno != EINTR)
  516.         return count;
  517.     }
  518. }
  519.  
  520. _G_fpos_t filebuf::sys_seek(_G_fpos_t offset, _seek_dir dir)
  521. {
  522.     return ::lseek(fd(), offset, (int)dir);
  523. }
  524.  
  525. _G_ssize_t filebuf::sys_write(const void *buf, _G_size_t n)
  526. {
  527.     long to_do = n;
  528.     while (to_do > 0) {
  529. #ifdef atarist
  530.     _G_ssize_t count = ::_write(fd(), buf, to_do);
  531. #else
  532.     _G_ssize_t count = ::write(fd(), buf, to_do);
  533. #endif
  534.     if (count == EOF) {
  535.         if (errno == EINTR)
  536.         continue;
  537.         else {
  538.         _flags |= _S_ERR_SEEN;
  539.         break;
  540.         }
  541.     }
  542.     to_do -= count;
  543.     buf = (void*)((char*)buf + count);
  544.     }
  545.     n -= to_do;
  546.     if (_fb._offset >= 0)
  547.     _fb._offset += n;
  548.     return n;
  549. }
  550.  
  551. int filebuf::sys_stat(void* st)
  552. {
  553.     return ::_fstat(fd(), (struct stat*)st);
  554. }
  555.  
  556. int filebuf::sys_close()
  557. {
  558.     return ::close(fd());
  559. }
  560.  
  561. _G_size_t filebuf::xsputn(const char *s, _G_size_t n)
  562. {
  563.     if (n == 0)
  564.     return 0;
  565.     // This is an optimized implementation.
  566.     // If the amount to be written straddles a block boundary
  567.     // (or the filebuf is unbuffered), use sys_write directly.
  568.  
  569.     _G_size_t to_do = n;
  570.     int must_flush = 0;
  571.     // First figure out how much space is available in the buffer.
  572.     _G_size_t count = _epptr - _pptr; // Space available.
  573.     if (linebuffered() && (_flags & _S_CURRENTLY_PUTTING)) {
  574.     count =_ebuf - _pptr;
  575.     if (count >= n) {
  576.         for (register const char *p = s + n; p > s; ) {
  577.         if (*--p == '\n') {
  578.             count = p - s + 1;
  579.             must_flush = 1;
  580.             break;
  581.         }
  582.         }
  583.     }
  584.     }
  585.     // Then fill the buffer.
  586.     if (count > 0) {
  587.     if (count > to_do)
  588.         count = to_do;
  589.     if (count > 20) {
  590.         memcpy(pptr(), s, count);
  591.         s += count;
  592.     }
  593.     else {
  594.         register char *p = pptr();;
  595.         for (register _G_size_t i = count+1; --i > 0; ) *p++ = *s++;
  596.     }
  597.     pbump(count);
  598.     to_do -= count;
  599.     }
  600.     if (to_do + must_flush > 0) {
  601.     // Next flush the (full) buffer.
  602.     if (__overflow(this, EOF) == EOF)
  603.         return n - to_do;
  604.  
  605.     // Try to maintain alignment: write a whole number of blocks.
  606.     // dont_write is what gets left over.
  607.     _G_size_t block_size = _ebuf - _base;
  608.     _G_size_t dont_write = block_size >= 128 ? to_do % block_size : 0;
  609.  
  610.     _G_ssize_t count = to_do - dont_write;
  611.     if (do_write(s, count) == EOF)
  612.         return n - to_do;
  613.     to_do = dont_write;
  614.  
  615.     // Now write out the remainder.  Normally, this will fit in the
  616.     // buffer, but it's somewhat messier for line-buffered files,
  617.     // so we let streambuf::sputn handle the general case.
  618.     if (dont_write)
  619.         to_do -= streambuf::sputn(s+count, dont_write);
  620.     }
  621.     return n - to_do;
  622. }
  623.  
  624. _G_size_t filebuf::xsgetn(char *s, _G_size_t n)
  625. {
  626.     // FIXME: OPTIMIZE THIS (specifically, when unbuffered()).
  627.     return streambuf::xsgetn(s, n);
  628. }
  629.  
  630. // Non-ANSI AT&T-ism:  Default open protection.
  631. const int filebuf::openprot = 0644;
  632.